package com.learn.jackson.annotation.jsonTypeId;

import com.fasterxml.jackson.annotation.JsonTypeId;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author XuYang
 * @since 2025/6/10 12:16
 */
@Data
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@AllArgsConstructor(staticName = "of")
public class JsonTypeIdEntity {

    Admin admin;

    @Data
    @FieldDefaults(level = AccessLevel.PRIVATE)
    @JsonTypeInfo(
        use = JsonTypeInfo.Id.CUSTOM,
        property = "type"
    )
    @JsonTypeIdResolver(CustomTypeIdResolver.class)
    public abstract static class Admin {
    }

    @Data
    @NoArgsConstructor
    @FieldDefaults(level = AccessLevel.PRIVATE)
    @AllArgsConstructor(staticName = "of")
    public static class Dog
        extends Admin
    {
        Long id;

        @JsonTypeId
        public String getClassType() {
            return "dog";
        }
    }

    @Data
    @NoArgsConstructor
    @FieldDefaults(level = AccessLevel.PRIVATE)
    @AllArgsConstructor(staticName = "of")
    public static class Cat
        extends Admin
    {
        String name;

        @JsonTypeId
        public String getClassType() {
            return "cat";
        }
    }

    public static class CustomTypeIdResolver
        extends TypeIdResolverBase
    {
        private static final Map<String, Class<?>> idToClass = new HashMap<>();
        private static final Map<Class<?>, String> classToId = new HashMap<>();

        static {
            idToClass.put("dog", Dog.class);
            idToClass.put("cat", Cat.class);

            classToId.put(Dog.class, "car123");
            classToId.put(Cat.class, "truck456");
        }

        @Override
        public JavaType typeFromId(DatabindContext context, String id) throws IOException {
            Class<?> clazz = idToClass.get(id);
            if (clazz == null) {
                throw new IllegalStateException("Unknown type id: " + id);
            }
            return context.constructType(clazz);
        }

        @Override
        public String idFromValue(Object value) {
            String id = classToId.get(value.getClass());
            if (id == null) {
                throw new IllegalArgumentException("Unknown class: " + value.getClass());
            }
            return id;
        }

        @Override
        public String idFromValueAndType(Object value, Class<?> suggestedType) {
            return "";
        }

        @Override
        public JsonTypeInfo.Id getMechanism() {
            return JsonTypeInfo.Id.CUSTOM;
        }
    }
}
